﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：b2096c56-e0c2-490e-bbba-2242446de6cc
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：MatchContentType
//Namespace：RuanRang.Core.Utility
//Functions：MatchContentType  
//Created by Kevin Wang at 2013/2/4 20:33:26 http://blog.9un.org
//============================================================================== 

using System;
using System.Drawing;
using System.Threading;

namespace RuanRang.Core.Utility
{
    /// <summary>
    /// 有关颜色的方法类
    /// 主要用于计算窗口背景
    /// </summary>
    public class ColorFunctions
    {
        #region Delegates

        public delegate void ComputeCompleteCallback(Color color);

        #endregion

        /// <summary>
        /// 两种颜色不同的阀值
        /// </summary>
        public static readonly double CoverColorDiff = 0.2;

        /// <summary>
        /// 亮度调整幅度
        /// </summary>
        public static readonly double ReviseParameter = 0.1;

        /// <summary>
        /// 用于调整封面右边取色区域的大小，具体表示右边区域宽度占图片宽度的百分比
        /// </summary>
        public static readonly double RightSideWidth = 0.1;

        /// <summary>
        /// 判断颜色是否太暗
        /// </summary>
        public static readonly double TooDark = 0.15;

        /// <summary>
        /// 判断颜色是否太暗亮
        /// </summary>
        public static readonly double TooBright = 0.4;

        /// <summary>
        /// 色相偏移
        /// </summary>
        public static readonly double HueOffset = 10;

        /// <summary>
        /// 进度条颜色调整参数
        /// </summary>
        public static readonly double ProgressBarReviseParameter = 0.2;

        /// <summary>
        /// 判断颜色饱和度是否太低
        /// </summary>
        public static readonly double NotSaturateEnough = 0.4;

        /// <summary>
        /// 判断颜色饱和度是否接近0
        /// </summary>
        public static readonly double AlmostZeroSaturation = 0.001;

        /// <summary>
        /// 从图片中获取背景颜色
        /// </summary>
        /// <param name="image">图片</param>
        /// <param name="callback">计算完成后调用的委托</param>
        public static void GetImageColorForBackgroundAsync(Bitmap image, ComputeCompleteCallback callback)
        {
            const int bytesPerPixel = 3;
            //Used for tally
            var width = image.Width;
            var height = image.Height;
            var bmp = new Bitmap(image);

            ThreadPool.QueueUserWorkItem(state =>
                {
                    //计算颜色的均值
                    var r = 0;
                    var g = 0;
                    var b = 0;
                    var sum = width * height;
                    var iBytes = width * height * bytesPerPixel;
                    var rgbValues = new byte[iBytes];
                    var offset = 0;
                    for (var x = 0; x < width; x++)
                    {
                        for (var y = 0; y < height; y++)
                        {
                            var clr = bmp.GetPixel(x, y);
                            r += clr.R;
                            rgbValues[offset++] = (byte)r;
                            g += clr.G;
                            rgbValues[offset++] = (byte)g;
                            b += clr.B;
                            rgbValues[offset++] = (byte)b;
                        }
                    }

                    //Calculate average
                    r /= sum;
                    g /= sum;
                    b /= sum;
                    bmp.Dispose();
                    var color1 = Color.FromArgb(r, g, b);

                    ////计算颜色的均值
                    ////获取矩阵
                    //// Lock the bitmap's bits.  锁定位图
                    //var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                    //var bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                    //// Get the address of the first line.获取首行地址
                    //var ptr = bmpData.Scan0;

                    //// Declare an array to hold the bytes of the bitmap.定义数组保存位图
                    //var bytes = width * height * bytesPerPixel;
                    //var rgbValues = new byte[bytes];
                    //// Copy the RGB values into the array.复制RGB值到数组
                    //Marshal.Copy(ptr, rgbValues, 0, bytes);

                    //// Unlock the bits.解锁
                    //bmp.UnlockBits(bmpData);

                    //var sum = width * height;
                    //int r = 0, g = 0, b = 0;
                    //var offset = 0;
                    //for (var i = 0; i < height; i++)
                    //{
                    //    for (var j = 0; j < width; j++)
                    //    {
                    //        r += rgbValues[offset++];
                    //        g += rgbValues[offset++];
                    //        b += rgbValues[offset++];
                    //    }
                    //}
                    
                    
                    //r = r / sum;
                    //g = g / sum;
                    //b = b / sum;
                    //var color1 = Color.FromArgb((byte)r, (byte)g, (byte)b);

                    //计算图片右部的颜色均值
                    r = 0;
                    g = 0;
                    b = 0;
                    var jstart = (int)(width * (1 - 0.1));
                    sum = (width - jstart) * width;
                    for (var i = 0; i < height; i++)
                    {
                        for (var j = jstart; j < width; j++)
                        {
                            r += rgbValues[(i * width + j) * bytesPerPixel + 0];
                            g += rgbValues[(i * width + j) * bytesPerPixel + 1];
                            b += rgbValues[(i * width + j) * bytesPerPixel + 2];
                        }
                    }
                    r = r / sum;
                    g = g / sum;
                    b = b / sum;
                    var color2 = Color.FromArgb((byte)r, (byte)g, (byte)b);

                    //根据上面计算出来的两个颜色计算最终颜色
                    var hsl1 = new HSLColor(color1);
                    var hsl2 = new HSLColor(color2);
                    hsl1.Hue += 10;
                    if (IsNotSaturateEnough(hsl1) && !IsAlmostZeroSaturation(hsl1))
                        hsl1.Saturation += 0.2;
                    callback(Revise(hsl1, hsl2).ToRgb());
                });
        }

        /// <summary>
        /// 两种颜色差异是否足够大
        /// </summary>
        /// <param name="c1">颜色1</param>
        /// <param name="c2">颜色2</param>
        /// <returns>Boolean值</returns>
        public static bool IsTooMuchDiff(HSLColor c1, HSLColor c2)
        {
            return Difference(c1, c2) > CoverColorDiff;
            //return Math.Abs((c1.R - c2.R) * (c1.R - c2.R) + (c1.G - c2.G) * (c1.G - c2.G) + (c1.B - c2.B) * (c1.B - c2.B)) > CoverColorDiff;
        }

        /// <summary>
        /// 计算两种颜色的差异。0为无差异，4.2为差异最大值
        /// </summary>
        /// <param name="c1">颜色1</param>
        /// <param name="c2">颜色2</param>
        /// <returns>差异值</returns>
        public static double Difference(HSLColor c1, HSLColor c2)
        {
            var alphaDiff = Math.Abs(c1.Alpha - c2.Alpha);
            var hueDiff = Math.Min(Math.Abs(c1.Hue - c2.Hue),
                                      Math.Min(Math.Abs(c1.Hue + 360 - c2.Hue), Math.Abs(c1.Hue - c2.Hue - 360)));
            var saturationDiff = Math.Abs(c1.Saturation - c2.Saturation);
            var lightnessDiff = Math.Abs(c1.Lightness - c2.Lightness);
            if (alphaDiff + saturationDiff + lightnessDiff > CoverColorDiff)
                return saturationDiff + lightnessDiff;
            return hueDiff / 150 * Math.Min(c1.Saturation, c2.Saturation) *
                   (0.5 - Math.Max(Math.Abs(c1.Lightness - 0.5), Math.Abs(c2.Lightness - 0.5))) * 2;
            //return (Math.Abs(c1.Alpha - c2.Alpha) + HueDiff / 150 + Math.Abs(c1.Saturation - c2.Saturation) + Math.Abs(c1.Lightness - c2.Lightness));
        }

        /// <summary>
        /// 颜色饱和度是否太低
        /// </summary>
        /// <param name="color"> </param>
        /// <returns>Boolean值</returns>
        public static bool IsNotSaturateEnough(HSLColor color)
        {
            return color.Saturation < NotSaturateEnough;
        }

        /// <summary>
        /// 颜色饱和度是否接近0
        /// </summary>
        /// <param name="color"> </param>
        /// <returns>Boolean值</returns>
        public static bool IsAlmostZeroSaturation(HSLColor color)
        {
            return color.Saturation < AlmostZeroSaturation;
        }

        /// <summary>
        /// 颜色是否太暗
        /// </summary>
        /// <param name="color"> </param>
        /// <returns>Boolean值</returns>
        public static bool IsTooDark(HSLColor color)
        {
            return color.Lightness < TooDark;
        }

        /// <summary>
        /// 颜色是否太亮
        /// </summary>
        /// <returns>Boolean值</returns>
        public static bool IsTooBright(HSLColor color)
        {
            return color.Lightness > TooBright;
        }

        /// <summary>
        /// 反色
        /// </summary>
        /// <param name="color"> </param>
        /// <returns>反色</returns>
        public static HSLColor Reverse(HSLColor color)
        {
            var rgb = color.ToRgb();
            return new HSLColor(Color.FromArgb(rgb.A, (byte)(255 - rgb.R), (byte)(255 - rgb.G), (byte)(255 - rgb.B)));
            //return new HSLColor(hsvColor.Alpha, hsvColor.Hue + 180, 1 - hsvColor.Saturation, 1 - hsvColor.Lightness);
        }

        /// <summary>
        /// 颜色修正
        /// </summary>
        /// <param name="color1">待修正色</param>
        /// <param name="color2">参照色</param>
        /// <returns>修正色</returns>
        public static HSLColor Revise(HSLColor color1, HSLColor color2)
        {
            var newcolor = new HSLColor(color1.ToRgb());
            while (IsTooBright(newcolor) ||
                   !IsTooMuchDiff(newcolor, color2) && !IsTooDark(newcolor) && newcolor.Lightness > 0)
                newcolor = ReviseDarker(newcolor);
            if (!IsTooDark(newcolor)) return newcolor;
            newcolor = ReviseBrighter(color1);
            while (IsTooDark(newcolor) ||
                   !IsTooMuchDiff(newcolor, color2) && !IsTooBright(newcolor) && newcolor.Lightness < 1)
                newcolor = ReviseBrighter(newcolor);
            if (!IsTooBright(newcolor)) return newcolor;
            if (IsTooBright(color1))
                return ReviseVeryBright(color1);
            return IsTooDark(color1) ? ReviseVeryDark(color1) : color1;
        }

        /// <summary>
        /// 无参照色时的颜色修正
        /// </summary>
        /// <param name="color">待修正色</param>
        /// <returns>修正色</returns>
        public static HSLColor Revise(HSLColor color)
        {
            if (IsTooDark(color))
                return ReviseBrighter(color);
            var newcolor = ReviseDarker(color);
            return IsTooDark(newcolor) ? ReviseVeryDark(color) : newcolor;
        }

        /// <summary>
        /// 将颜色调整到能够接受的最高亮度
        /// </summary>
        /// <param name="color">待修正色</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseVeryBright(HSLColor color)
        {
            return ReviseBrighter(color, TooBright - color.Lightness);
        }

        /// <summary>
        /// 将颜色调整到能够接受的最低亮度
        /// </summary>
        /// <param name="color"> 待修正色</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseVeryDark(HSLColor color)
        {
            return ReviseDarker(color, color.Lightness - TooDark);
        }

        /// <summary>
        /// 将颜色调亮特定亮度
        /// </summary>
        /// <param name="color">待修正色 </param>
        /// <param name="brigher">调整的亮度</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseBrighter(HSLColor color, double brigher)
        {
            return new HSLColor(color.Alpha, color.Hue, color.Saturation, color.Lightness + brigher);
            //return Color.FromRgb(ReviseByteBigger(hsvColor.R), ReviseByteBigger(hsvColor.G), ReviseByteBigger(hsvColor.B));
        }

        /// <summary>
        /// 将颜色调亮一些
        /// </summary>
        /// <param name="color">待修正色</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseBrighter(HSLColor color)
        {
            return ReviseBrighter(color, ReviseParameter);
        }

        /// <summary>
        /// 将颜色调暗特定亮度
        /// </summary>
        /// <param name="color">待修正色</param>
        /// <param name="darker">调整的亮度</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseDarker(HSLColor color, double darker)
        {
            return new HSLColor(color.Alpha, color.Hue, color.Saturation, color.Lightness - darker);
        }

        /// <summary>
        /// 将颜色调暗一些
        /// </summary>
        /// <param name="color">待修正色</param>
        /// <returns>修正色</returns>
        public static HSLColor ReviseDarker(HSLColor color)
        {
            return ReviseDarker(color, ReviseParameter);
        }
    }

    /// <summary>
    /// HSL色彩空间的颜色类
    /// </summary>
    public class HSLColor
    {
        private double _a, _h;
        private double _l;
        private double _s;


        /// <summary>
        /// 无参构造函数
        /// </summary>
        public HSLColor()
            : this(1, 0, 0, 0)
        {
        }

        /// <summary>
        /// HSL构造函数
        /// </summary>
        /// <param name="hue">色相</param>
        /// <param name="saturation">饱和度</param>
        /// <param name="lightness">亮度</param>
        public HSLColor(double hue, double saturation, double lightness)
            : this(1, hue, saturation, lightness)
        {
        }

        /// <summary>
        /// AHSL构造函数
        /// </summary>
        /// <param name="alpha">Alpha通道</param>
        /// <param name="hue">色相</param>
        /// <param name="saturation">饱和度</param>
        /// <param name="lightness">亮度</param>
        public HSLColor(double alpha, double hue, double saturation, double lightness)
        {
            Alpha = alpha;
            Hue = hue;
            Saturation = saturation;
            Lightness = lightness;
        }

        /// <summary>
        /// 由RGB颜色类Color构造一个HSLColor的实例
        /// </summary>
        /// <param name="color">RGB颜色</param>
        public HSLColor(Color color)
        {
            FromRgb(color);
        }

        /// <summary>
        /// 不透明度。范围：0~1，1为不透明，0为透明
        /// </summary>
        public double Alpha
        {
            get { return _a; }
            set
            {
                if (value > 1)
                    _a = 1;
                else if (value < 0)
                    _a = 0;
                else _a = value;
            }
        }

        /// <summary>
        /// 色相。范围：0~359.9999999。特殊颜色：红：0    黄：60    绿：120   青：180   蓝：240   洋红：300
        /// </summary>
        public double Hue
        {
            get { return _h; }
            set
            {
                if (value >= 360)
                    _h = value % 360;
                else if (value < 0)
                    _h = value - Math.Floor(value / 360) * 360;
                else _h = value;
            }
        }

        /// <summary>
        /// 饱和度。范围：0~1。亮度0.5时，0为灰色，1为彩色
        /// </summary>
        public double Saturation
        {
            get { return _s; }
            set
            {
                if (value > 1)
                    _s = 1;
                else if (value < 0)
                    _s = 0;
                else _s = value;
            }
        }

        /// <summary>
        /// 亮度。范围：0~1。0为黑色，1为彩色
        /// </summary>
        public double Lightness
        {
            get { return _l; }
            set
            {
                if (value > 1)
                    _l = 1;
                else if (value < 0)
                    _l = 0;
                else _l = value;
            }
        }

        /// <summary>
        /// RGB色彩空间转换
        /// </summary>
        /// <param name="color">RGB颜色</param>
        public void FromRgb(Color color)
        {
            _a = (double)color.A / 255;
            var r = (double)color.R / 255;
            var g = (double)color.G / 255;
            var b = (double)color.B / 255;

            var min = Math.Min(r, Math.Min(g, b));
            var max = Math.Max(r, Math.Max(g, b));
            var distance = max - min;

            _l = (max + min) / 2;
            _s = 0;
            if (!(distance > 0)) return;
            _s = _l < 0.5 ? distance / (max + min) : distance / ((2 - max) - min);
            var tempR = (((max - r) / 6) + (distance / 2)) / distance;
            var tempG = (((max - g) / 6) + (distance / 2)) / distance;
            var tempB = (((max - b) / 6) + (distance / 2)) / distance;
            double hT;
            if (Math.Abs(r - max) <= 0)
                hT = tempB - tempG;
            else if (Math.Abs(g - max) <= 0)
                hT = (1.0 / 3 + tempR) - tempB;
            else
                hT = (2.0 / 3 + tempG) - tempR;
            if (hT < 0)
                hT += 1;
            if (hT > 1)
                hT -= 1;
            _h = hT * 360;
        }

        /// <summary>
        /// 转换到RGB色彩空间
        /// </summary>
        /// <param name="hsl">HSL颜色</param>
        /// <returns>转换后的RGB颜色</returns>
        private static Color ToRgb(HSLColor hsl)
        {
            byte a = (byte)Math.Round(hsl.Alpha * 255), r, g, b;
            if (Math.Abs(hsl.Saturation - 0F) <= 0)
            {
                r = (byte)Math.Round(hsl.Lightness * 255);
                g = r;
                b = r;
            }
            else
            {
                var vH = hsl.Hue / 360;
                var v2 = hsl.Lightness < 0.5
                                ? hsl.Lightness * (1 + hsl.Saturation)
                                : (hsl.Lightness + hsl.Saturation) - (hsl.Lightness * hsl.Saturation);
                var v1 = 2 * hsl.Lightness - v2;
                r = (byte)Math.Round(255 * HueToRgb(v1, v2, vH + 1.0 / 3));
                g = (byte)Math.Round(255 * HueToRgb(v1, v2, vH));
                b = (byte)Math.Round(255 * HueToRgb(v1, v2, vH - 1.0 / 3));
            }
            return Color.FromArgb(a, r, g, b);
        }

        /// <summary>
        /// 这个……我也不懂
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <param name="vH"></param>
        /// <returns></returns>
        private static double HueToRgb(double v1, double v2, double vH)
        {
            if (vH < 0) vH += 1;
            if (vH > 1) vH -= 1;
            if (6 * vH < 1) return v1 + ((v2 - v1) * 6 * vH);
            if (2 * vH < 1) return v2;
            if (3 * vH < 2) return v1 + (v2 - v1) * (2.0 / 3 - vH) * 6;
            return v1;
        }

        /// <summary>
        /// 转换到RGB色彩空间
        /// </summary>
        /// <returns>RGB颜色</returns>
        public Color ToRgb()
        {
            return ToRgb(this);
        }

        /// <summary>
        /// ToString()方法，已重写
        /// </summary>
        /// <returns>ARGB颜色值</returns>
        public override string ToString()
        {
            return ToRgb().ToString();
        }
    }
}